/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#ifndef ATTRIBUTESMODEL_H
#define ATTRIBUTESMODEL_H

#include <QAbstractTableModel>
#include <QUndoStack>
#include "validator.h"
#include "pythonvalidator.h"
#include "memorynode.h"
#include "nodesmodel.h"
#include "attributevalidator.h"
class MemoryNode; // fwd decl
class Attribute;
class AttributeContainer;
struct INodeRuleValidator;

class AttributesModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    enum CustomRoles
    {
      InternalPointer = Qt::UserRole,
    };
    explicit AttributesModel(AttributeContainer* root_item, NodesModel* nodes_model, QUndoStack* undoStack, QObject* parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role) override;

    bool addAttribute(const QModelIndex& parent, const QString name, const QString val = "");
    bool addAttribute(const QModelIndex& parent, std::unique_ptr<Attribute> attribute);
    std::unique_ptr<Attribute> removeAttribute(const QModelIndex& parent, const QString name);
    std::unique_ptr<Attribute> removeAttribute(const QModelIndex& parent, int position);

    bool addAttributeContainer(const QModelIndex& parent, std::unique_ptr<AttributeContainer> attribute_container);
    bool addAttributeContainer(int position, const QModelIndex& parent, std::unique_ptr<AttributeContainer> attribute_container);
    bool addAttributeContainer(int position, const QModelIndex& parent, const QString name);
    bool addAttributeContainer(const QModelIndex& parent, const QString name);

    bool addAttributeContainers(const QModelIndex& parent, const std::vector<std::string> names);
    std::unique_ptr<AttributeContainer> removeAttributeContainer(const QModelIndex& parent, const QString name);
    std::unique_ptr<AttributeContainer> removeAttributeContainer(const QModelIndex& parent, int position);


    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;

    QVariant getAttributeValue(const QModelIndex& index, int role = Qt::DisplayRole) const;

    const NodeRangeValidator* getRangeValidator(const QModelIndex& index) const;
    const NodeRegexValidator* getRegexValidator(const QModelIndex& index) const;
    const NodeEnumValidator* getEnumValidator(const QModelIndex& index) const;
    const PyNodeEnumValidator* getPyEnumValidator(const QModelIndex& index) const;



    AttributeContainer* getContainer(const QModelIndex& index) const;
    AttributeContainer* getParentContainer(const QModelIndex& index) const;
    Attribute* getAttribute(const QModelIndex& index) const;
    NodesModel* getNodesModel() const;
    static AttributesModel* getAttributesModel(const QModelIndex& index);
    QModelIndex getIndex(Attribute* attribute) const;
    QModelIndex getIndex(AttributeContainer* attribute_container) const;

    MemoryNode* getParentMemoryNode();

    bool isAttribute(const QModelIndex& index) const;
    bool isContainer(const QModelIndex& index) const;

    bool isEditable(const QModelIndex& index) const;
public Q_SLOTS:
    void notifyTreeDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
    void notifyTreeDataChanged(const QModelIndex &parent, int first, int last);
    void notifyTreeDataChanged();

private:
    template<typename Validator>
    const Validator* getValidator(Validator* requested_validator, const QModelIndex& index) const
    {
        requested_validator = nullptr;
        if(!isAttribute(index))
            return requested_validator;
        Attribute* attr = getAttribute(index);
        return attr->getValidator()->getValidator(requested_validator);
    }
    AttributeContainer *rootItem;
    NodesModel* nodes_model;
    QUndoStack* undoStack;
private:
    // dont use these
    using QAbstractItemModel::insertRow;
    using QAbstractItemModel::insertRows;
};

#endif // ATTRIBUTESMODEL_H
